package net.openadventure.location;

import java.awt.Polygon;
import java.awt.geom.Area;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;

import net.openadventure.WalkableArea;
import net.openadventure.hotspot.Hotspot;

/**
 * Location represents a location or "room" in the game world. A Location may
 * have any number of background images associated with it. A Location may have
 * any number of walkable areas associated with it. A walkable area is an area
 * in which the player can move freely. A Location may also have any number of
 * hotspots associated with it. A hotspot is an area which triggers an event
 * when the player clicks or walks on it. Locations contain references to
 * "Scripts" which are activated by location events.
 * 
 * @author Paul
 * 
 */
public class Location {

	// a mapping of named background images for this Location. Each name maps to
	// a relative path to the corresponding image file.
	private final LinkedHashMap<String, String> backgroundImagePaths = new LinkedHashMap<String, String>();

	// a mapping of named walkable areas in this location. Walkable areas are
	// polygons within which the player can walk freely.
	private final LinkedHashMap<String, WalkableArea> walkableAreas = new LinkedHashMap<String, WalkableArea>();

	// a mapping of named hot spots to this location. These can optionally fire
	// off one or more of a list of pre-coded actions including playerExit,
	// display pop-up, etc.
	private final LinkedHashMap<String, Hotspot> hotspots = new LinkedHashMap<String, Hotspot>();

	// a mapping of named entrances to this location. An Entrance is two sets of
	// coordinates that describe a possible player entry point to this Location.
	private final LinkedHashMap<String, Entrance> entrances = new LinkedHashMap<String, Entrance>();

	private String[] onLoadScript = new String[0];

	// the name which describes this Location
	private final String name;

	private String activeBackgroundImagePath = "";

	private WalkableArea currentWalkableArea = null;

	/**
	 * Constructs a Location with no background images, walkable areas, or
	 * hotspots
	 * 
	 * @param name
	 *            the name which describes this Location
	 * @throws NullPointerException
	 *             if the name parameter is null
	 */
	public Location(String name) {
		// Check parameter
		if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		}

		this.name = name;
	}

	/**
	 * Adds a named background image to this Location. Neither parameter may be
	 * null.
	 * 
	 * @param name
	 *            the name which describes this background image
	 * @param path
	 *            the relative path to the image file
	 * @throws NullPointerException
	 *             if the name parameter is null
	 * @throws NullPointerException
	 *             if the path parameter is null
	 */
	public void addBackgroundImagePath(String name, String path) {
		// Check parameters
		if (name == null && path == null) {
			throw new NullPointerException(
					"Parameters name and path can not be null.");
		} else if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		} else if (path == null) {
			throw new NullPointerException("Parameter path can not be null.");
		}

		backgroundImagePaths.put(name, path);

		activeBackgroundImagePath = path;
	}

	/**
	 * Adds a named walkable area to the Location. Neither parameter may be
	 * null.
	 * 
	 * @param name
	 *            the name which describes this background image
	 * @param walkableArea
	 *            the WalkableArea which describes the bounds of a walkable area
	 * @throws NullPointerException
	 *             if the name parameter is null
	 * @throws NullPointerException
	 *             if the walkableArea parameter is null
	 */
	public void addWalkableArea(String name, WalkableArea walkableArea) {
		// Check parameters
		if (name == null && walkableArea == null) {
			throw new NullPointerException(
					"Parameters name and walkableArea can not be null.");
		} else if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		} else if (walkableArea == null) {
			throw new NullPointerException(
					"Parameter walkableArea can not be null.");
		}

		walkableAreas.put(name, walkableArea);
		currentWalkableArea = walkableArea;
	}

	/**
	 * Adds a hotspot to this location. The name parameter contains the string
	 * that will be used to refer to this hotspot. If this location already
	 * contains a hotspot which uses this name, the old hotspot is replaced by
	 * the new one. Neither the name nor the hotspot can be null.
	 * 
	 * @param name
	 *            The name used to access this hotspot.
	 * @param hotspot
	 *            The new hotspot.
	 * @throws NullPointerException
	 *             if the name parameter is null
	 * @throws NullPointerException
	 *             if the hotspot parameter is null
	 */
	public void addHotspot(String name, Hotspot hotspot) {
		// Check parameters
		if (name == null && hotspot == null) {
			throw new NullPointerException(
					"Parameters name and hotspot can not be null.");
		} else if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		} else if (hotspot == null) {
			throw new NullPointerException("Parameter hotspot can not be null.");
		}

		hotspots.put(name, hotspot);
	}

	public Hotspot getHotSpot(String name) {
		return hotspots.get(name);
	}

	/**
	 * Adds a named Entrance to this Location. Neither parameter may be null.
	 * 
	 * @param name
	 *            the name which describes this Entrance
	 * @param entrance
	 *            the Entrance object
	 * @throws NullPointerException
	 *             if the name parameter is null
	 * @throws NullPointerException
	 *             if the entrance parameter is null
	 */
	public void addEntrance(String name, Entrance entrance) {
		// Check parameters
		if (name == null && entrance == null) {
			throw new NullPointerException(
					"Parameters name and entrance can not be null.");
		} else if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		} else if (entrance == null) {
			throw new NullPointerException(
					"Parameter entrance can not be null.");
		}

		entrances.put(name, entrance);
	}

	/**
	 * Gets the name which describes this Location
	 * 
	 * @return the name which describes this Location
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gets the path to the background image with the given name
	 * 
	 * @param name
	 *            the name of the image to get the path for
	 * @return the relative path to the requested image, or null if an image by
	 *         that name wasn't found
	 * @throws NullPointerException
	 *             if the name parameter is null
	 */
	public String getBackgroundImagePath(String name) {
		// Check parameter
		if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		}

		return backgroundImagePaths.get(name);
	}

	/**
	 * This is a convienience method for always getting the location's current
	 * background image. By default, this is the last path added to the
	 * location.
	 * 
	 * @return the path to the active background image
	 */
	public String getActiveBackgroundImagePath() {
		return activeBackgroundImagePath;
	}

	/**
	 * Gets the Entrance object with the given name
	 * 
	 * @param name
	 *            the name of the Entrance to get
	 * @return the Entrance associated with the given name, or null if an
	 *         Entrance by that name wasn't found
	 */
	public Entrance getEntrance(String name) {
		// Check parameter
		if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		}

		return entrances.get(name);
	}

	/**
	 * Checks if the given coordinates are located within a background hotspot.
	 * If the coordinates are contained in a hotspot, that hotspot is returned.
	 * If the coordinates are not contained in a hotspot, null is returned. If
	 * the coordinates are located in more than one hotspot, then the hotspot
	 * that was added to this location latest (the hotspot on top) is returned.
	 * 
	 * @param x
	 *            The x coordinate
	 * @param y
	 *            The y coordinate
	 * @return The hotspot containing the given coordinates.
	 */
	public Hotspot checkCoordinatesForHotspot(int x, int y) {
		// Loop through each hotspot in insert order
		for (Iterator<String> it = hotspots.keySet().iterator(); it.hasNext();) {
			String key = it.next();
			Hotspot hotspot = hotspots.get(key);

			// If a hotspot contains the given coordinates, return it.
			// Giving preference to hotspots that were inserted later.
			if (hotspot.getArea().contains(x, y)) {
				return hotspot;
			}
		}
		return null;
	}

	public WalkableArea checkCoordinatesForWalkableArea(int x, int y) {
		// Loop through each walkable area in insert order
		for (Iterator<WalkableArea> it = walkableAreas.values().iterator(); it
				.hasNext();) {
			WalkableArea walkableArea = it.next();

			if (walkableArea.getPolygon().contains(x, y)) {
				return walkableArea;
			}
		}
		return null;
	}

	/**
	 * Checks if the given coordinates are located within a given walkable area.
	 * If the coordinates are contained in the walkable area, the method returns
	 * true. If the coordinates are not contained in the walkable area, the
	 * method returns false.
	 * 
	 * @param name
	 *            The name of the walkable area to check the coordinates against
	 * @param x
	 *            The x coordinate
	 * @param y
	 *            The y coordinate
	 * @return TRUE if and only if the coordinates are contained in the walkable
	 *         area.
	 * @throws NullPointerException
	 *             if the name parameter is null
	 */
	public boolean checkCoordinatesForWalkableArea(String name, int x, int y) {
		// Check parameter
		if (name == null) {
			throw new NullPointerException("Parameter name can not be null.");
		}

		// If the walkable area exists and contains the given coordinates,
		// return
		// true.
		if (walkableAreas.get(name) != null
				&& walkableAreas.get(name).getPolygon().contains(x, y)) {
			return true;
		}

		return false;
	}

	// public WalkableArea checkFootPrintForWalkableArea(Rectangle2D footprint)
	// {
	// // Loop through each walkable area in insert order
	// for (Iterator<WalkableArea> it = walkableAreas.values().iterator();
	// it.hasNext();) {
	// WalkableArea walkableArea = it.next();
	//
	// if (walkableArea.getPolygon().contains(footprint)) {
	// return walkableArea;
	// }
	// }
	// return null;
	// }

	public Hotspot[] checkPolygonForHotspots(Polygon shape) {
		ArrayList<Hotspot> intersectingHotspots = new ArrayList<Hotspot>();

		// Loop through each hotspot in insert order
		for (Iterator<String> it = hotspots.keySet().iterator(); it.hasNext();) {
			String key = it.next();
			Hotspot hotspot = hotspots.get(key);

			// If a hotspot intersects the shape add it to the return list
			Area intersection = new Area(hotspot.getArea());
			intersection.intersect(new Area(shape));
			if (!intersection.isEmpty()) {
				intersectingHotspots.add(hotspot);
			}
		}

		Hotspot[] hotspotArray = new Hotspot[intersectingHotspots.size()];
		intersectingHotspots.toArray(hotspotArray);
		return hotspotArray;
	}

	public String[] getOnLoadScript() {
		return onLoadScript;
	}

	public void setOnLoadScript(String script) {
		String[] lines = script.split("\n");
		this.onLoadScript = lines;
	}

	public WalkableArea getCurrentWalkableArea() {
		return currentWalkableArea;
	}

	public void setCurrentWalkableArea(WalkableArea currentWalkableArea) {
		this.currentWalkableArea = currentWalkableArea;
	}
}
